package ru.positron;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.net.MalformedURLException;
import java.net.SocketTimeoutException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.jdom.Element;

import com.sun.syndication.feed.synd.SyndEntry;
import com.sun.syndication.feed.synd.SyndFeed;
import com.sun.syndication.feed.synd.SyndFeedImpl;
import com.sun.syndication.io.FeedException;
import com.sun.syndication.io.SyndFeedInput;
import com.sun.syndication.io.SyndFeedOutput;
import com.sun.syndication.io.XmlReader;


@SuppressWarnings("serial")
public class OsmRssSpatialFilter extends HttpServlet {
	static Logger LOG = Logger.getLogger( OsmRssSpatialFilter.class.getName() );

	public void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
		String sSourceUrl = req.getParameter("feed");
		double maxSizeLat, maxSizeLon;
		if(sSourceUrl==null) {
			LOG.warning("No feed provided");
			replyError(resp, 400, "Feed required");
			return;
		}
		if(sSourceUrl=="") {
			LOG.warning("Empty feed request");
			replyError(resp, 400, "Feed required");
			return;
		}
		if(!sSourceUrl.startsWith("http://")) sSourceUrl = "http://"+sSourceUrl;
		
		try {	maxSizeLat = Double.parseDouble( req.getParameter("slat") );
		} catch (RuntimeException e) { maxSizeLat = 20; }
		try {	maxSizeLon= Double.parseDouble( req.getParameter("slon") );
		} catch (RuntimeException e) { maxSizeLon = 40; }
		if(maxSizeLat!=20 || maxSizeLon!=40) LOG.info("Manual lat/lon limit override: "+maxSizeLat+"/"+maxSizeLon);
		
		URL sourceUrl;
		try {
			sourceUrl  = new URL(sSourceUrl);
		} catch (MalformedURLException e) {
			LOG.warning("Bad URL \""+sSourceUrl+"\": "+e);
			replyError(resp, 400, "Bad URL: \""+sSourceUrl+"\"");
	    	return;
		}
	      
	    long time0 = System.currentTimeMillis(), time1=time0;
	    InputStream is = null;
	    try {
	    	is = Utils.download(sourceUrl);
	    } catch (SocketTimeoutException te) {
	    	LOG.info("Timeout exception at "+(System.currentTimeMillis()-time0)+"ms from start");
	    	replyError(resp, 504, "Request to OSM server timed out");
	    	return;
	    } catch (Exception e) {
	    	LOG.log(Level.WARNING, "Exception while fetching feed:", e);
	    	replyError(resp, "Failed to fetch feed");
	    	return;
	    }
	    XmlReader reader = null;  
	    SyndFeed feed = null;
	    try {
			reader = new XmlReader(is);	    	
			feed = new SyndFeedInput().build(reader);
	    }  catch (FeedException fe) {
	    	LOG.log(Level.WARNING, "Processing feed caused exception: ", fe );
	    	replyError(resp, "Exception during source feed parse: ");
	    	return;
	    } catch (RuntimeException e) {
	    	LOG.warning("Unexpected exception "+e +" for feed=\""+sSourceUrl+"\"");
	    	replyError(resp, "Exception during source feed processing: "+e);
	    	return;
        } finally {
            if (reader != null)
                reader.close();
        }
		if (feed==null) {
			LOG.info("No feed recieved, quitting");
			return;
		}
		time1 = System.currentTimeMillis();	

	    int srcSize = feed.getEntries().size();
	    @SuppressWarnings("unchecked")
	    Iterator<SyndEntry> it = ((List<SyndEntry>)feed.getEntries()).iterator();
	    while (it.hasNext()) {
		//for (SyndEntry entry: srcList) {
			boolean toAdd = true;
			SyndEntry entry = it.next();
			try {
				@SuppressWarnings("unchecked")
				List<Element> extras = (List<Element>)entry.getForeignMarkup();
				for(Element extraElem: extras) {
					String tag=extraElem.getQualifiedName();
					if(! "georss:box".equals(tag) ) continue;
					//LOG.info( "Testing list contents: Name:"+extraElem.getName()+"; NS="+extraElem.getNamespacePrefix()+"; Text="+extraElem.getTextNormalize() );
					String[] sCoords= extraElem.getTextNormalize().split(" ") ;
					double[] coords = {Double.parseDouble(sCoords[0]), Double.parseDouble(sCoords[1]), 
							Double.parseDouble(sCoords[2]), Double.parseDouble(sCoords[3]) };
					if(coords[2]-coords[0]>maxSizeLat || coords[3]-coords[1]>maxSizeLon) {
						LOG.fine(String.format("Filtered out item: %s, %s (by %s)", entry.getPublishedDate(), entry.getTitle(), entry.getAuthor()));
						toAdd = false;
						
					}
				}
			} catch(RuntimeException e) {
				LOG.warning("Exception while checking coords: "+e);
				toAdd = true;
			}
			if(toAdd) {
				LOG.fine(String.format("Included item %s, %s (by %s)", entry.getPublishedDate(), entry.getTitle(), entry.getAuthor()));
				//generatedList.add(entry);				
			} else it.remove();
		}
		
	    SyndFeed outfeed = feed;//new SyndFeedImpl(feed.createWireFeed());
	    outfeed.setFeedType("atom_1.0");
	    resp.setContentType("application/atom+xml");
	    resp.setCharacterEncoding("UTF-8");
	    Writer wr = new OutputStreamWriter(resp.getOutputStream(), resp.getCharacterEncoding() );
	    SyndFeedOutput output = new SyndFeedOutput();
		try {
			output.output(outfeed, wr);
		} catch (FeedException e) {
			LOG.log(Level.WARNING, "Exception exporting feed: ",e);
		}
	    
	    long time2 = System.currentTimeMillis();	    
	    LOG.info("Kept "+outfeed.getEntries().size()+" entries from "+srcSize+
	    		". Time: "+(time1-time0)+" ms to build feed, "+(time2-time1)+" ms to filter and output");
	}
	
	private void replyError(HttpServletResponse resp, String err) {
		replyError(resp, 500, err);
	}
	private void replyError(HttpServletResponse resp, int code, String err) {
		try {
			resp.setStatus(code);
			resp.setContentType("text/plain");
			resp.getWriter().println(err);
		} catch(Exception e) {
			LOG.log(Level.WARNING, "Could not send error response: ", e);
		}
	}
	
}
